home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mntlib43 / mntlib / spawn.c < prev    next >
C/C++ Source or Header  |  1994-01-15  |  11KB  |  513 lines

  1. /*
  2.    89/03/02: ERS: added the "mode" argument for MS-DOS/Unix
  3.    compatibility. Added prototypes for GNU C.
  4.  
  5.    fixed spawnve: the original didn't pass the command tail
  6.    correctly, nor did it handle errno right. Also, this version
  7.    passes args in the environment as well as the command line,
  8.    using the ARGV= mechanism supported by crt0.s
  9.  
  10.    Written 89/01/10 by ERS. Original version Copyright (c) 1988 by
  11.    Memorial University of Newfoundland. This version is based upon
  12.    that original, but is substantially different. (In fact, there
  13.    probably isn't a single line of the original left.)
  14.  
  15.    Adapted 90/06/25 by ERS to MiNT.
  16.  
  17.    08 Apr 93 hohmuth
  18.     added support for ARGV standard extension allowing empty arguments
  19.  
  20.    uo, 3.5.93, throw away some static characterarrays (path[] and
  21.    cmd[]).
  22.  
  23.    10 Jul 1993 ole
  24.    added emulation for script execution.
  25.    This feature is controlable by the Environment variable "UNIXMODE".
  26.    The switch 's' in UNIXMODE says that executable text files with a
  27.    hash as their first character are interpretable.
  28.    Files which have only '#'  as their first character are interpreted
  29.    using /bin/sh
  30.    Files which have i.e '#!/usr/bin/awk -f' as their first character
  31.    are interpreted with "/usr/bin/awk -f <file>"
  32.  
  33.    17 Sep 1993 schwab
  34.    cleaned up emulation code for script execution.
  35.    File is only interpretable if first two bytes are "#!".
  36.    Don't modify errno if there is no error.
  37. */
  38.  
  39. #include    <stdarg.h>
  40. #include    <process.h>
  41. #include    <param.h>
  42. #include    <errno.h>
  43. #include    <osbind.h>
  44. #include    <mintbind.h>
  45. #include    <stdlib.h>
  46. #include    <time.h>
  47. #include    <string.h>
  48. #include    <unistd.h>
  49. #include    <support.h>
  50. #include    "lib.h"
  51.  
  52. #define TOS_ARGS 126
  53.  
  54. extern char **environ;
  55. extern int __mint;
  56.  
  57. /* comment this out, if you don't want script execution */
  58. #define HASH_BANG
  59.  
  60. #ifdef HASH_BANG
  61.  
  62. static char *const extensions[] = { "ttp", "prg", "tos", NULL };
  63.  
  64. static int interpret_script __PROTO((int mode, const char *path, const char *,
  65.                     char *const *argv, char *const *envp));
  66.  
  67. static int
  68. interpret_script(mode, path, _path, argv, envp)
  69.     int        mode;
  70.     const char *path;
  71.     const char *_path;
  72.     char    *const *argv;
  73.     char    *const *envp;
  74. {
  75.     char    tmppath[MAXPATHLEN];
  76.     char    *shell;
  77.     char    *shellargs;
  78.     char    **shellargv;
  79.     int        nargcount;
  80.     int        fd;
  81.     int        argcount;
  82.     int        i, rv;
  83.     char buf[1024], *bufp, *argp;
  84.     long r;
  85.  
  86.     /* path is already converted to dos */
  87.     if ((fd = (int)Fopen(path, 0)) < 0)
  88.       {
  89.         errno = -fd;
  90.         return -1;
  91.       }
  92.  
  93.     r = Fread (fd, (long) sizeof (buf) - 1L, buf);
  94.     Fclose (fd);
  95.     if (r < 0)
  96.       {
  97.         errno = -(int) r;
  98.         return -1;
  99.       }
  100.     buf[r] = 0;
  101.  
  102.     if (buf[0] == '#' && buf[1] == '!')
  103.       {
  104.         /* skip blanks */
  105.         bufp = buf + 2;
  106.         while (*bufp == '\t' || *bufp == ' ')
  107.           bufp++;
  108.  
  109.         /* read filename */
  110.         shell = bufp;
  111.         while (*bufp && *bufp != ' ' && *bufp != '\t'
  112.            && *bufp != '\r' && *bufp != '\n')
  113.           bufp++;
  114.  
  115.         if (*bufp && *bufp != '\r' && *bufp != '\n')
  116.           *bufp++ = 0;
  117.         /* else the null will be added below */
  118.  
  119.         /* 
  120.          * read arguments if any
  121.          */
  122.         argp = shellargs = bufp;
  123.         nargcount = 0;
  124.         i = 0;
  125.         while (*bufp && *bufp != '\r' && *bufp != '\n')
  126.           {
  127.         /* skip blanks */
  128.         while (*bufp == ' ' || *bufp == '\t')
  129.           bufp++;
  130.  
  131.         if (*bufp == '\r' || *bufp == '\n')
  132.           break;
  133.  
  134.         while (*bufp && *bufp != ' ' && *bufp != '\t'
  135.                && *bufp != '\r' && *bufp != '\n')
  136.           *argp++ = *bufp++;
  137.         *argp++ = '\0';
  138.         nargcount++;
  139.           }
  140.         *argp = '\0';
  141.         
  142.         if (*shell)
  143.           {
  144.         shell = _buffindfile (shell, getenv("PATH"), extensions, tmppath);
  145.         if (!shell)
  146.           {
  147.             errno = ENOENT;
  148.             return -1;    /* file not found */
  149.           }
  150.  
  151.         /* count old args */
  152.         for (i = 0; argv[i] != 0; i++);
  153.         argcount = i;
  154.  
  155.         shellargv = (char **)Malloc((argcount + nargcount + 2) * sizeof(char *));
  156.         if (!shellargv) {
  157.           errno = ENOMEM;
  158.           return -1;
  159.         }
  160.  
  161.         i = 0;
  162.         shellargv[i++] = shell;
  163.         while(*shellargs != '\0') {
  164.           shellargv[i++] = shellargs;
  165.           while (*shellargs++ != '\0');
  166.         }
  167.  
  168.         /* use the full pathname of the script instead of argv[0] */
  169.         shellargv[i++] = (char *) _path;
  170.         while (*++argv != NULL)
  171.           shellargv[i++] = *argv;
  172.         shellargv[i] = NULL;
  173.  
  174.         rv = _spawnve(mode, shell, shellargv, envp);
  175.         (void)Mfree(shellargv);
  176.         return rv;
  177.           }
  178.       }
  179.  
  180.     errno = ENOEXEC;
  181.     return -1;
  182. }
  183.  
  184. #endif /* HASH_BANG */
  185.  
  186. int
  187. _spawnve(mode, _path, argv, envp)
  188. int    mode;
  189. const char    *_path;
  190. char    *const *argv;
  191. char    *const *envp;
  192. {
  193.     char        path[MAXPATHLEN];
  194.     char        cmd[TOS_ARGS + 1];
  195.     size_t        cmlen;
  196.     size_t        enlen = 0;
  197.     size_t        left, min_left;
  198.     const char    *p;
  199.     char        *s, *t;
  200.     char        *env;
  201.     long        rval;
  202.     const char *pconv, *pc, *tmp;
  203.     size_t len, cnt;
  204.     int i, done;
  205. #ifdef HASH_BANG
  206.     char *const *_envp;
  207.     char *const *_argv;
  208. #endif
  209.  
  210.     if (mode != P_WAIT && mode != P_OVERLAY && mode != P_NOWAIT) {
  211.         errno = EINVAL;
  212.         return -1;
  213.     }
  214.     (void)_unx2dos(_path, path);    /* convert filename, if necessary */
  215.     if (!envp)
  216.         envp = environ;
  217.  
  218. #ifdef HASH_BANG
  219.         _envp = envp;
  220.         _argv = argv;
  221. #endif
  222.  
  223. /* try to find PCONVERT environment variable */
  224.     for (i = 0; (pconv = envp[i]) != 0; i++) {
  225.         if (! strncmp(pconv, "PCONVERT=", 9)) {
  226.             pconv += 9;
  227.             break;
  228.         }
  229.     }
  230.  
  231. /* count up space needed for environment */
  232.     for(cmlen = 0; argv[cmlen]; cmlen++)
  233.         enlen += strlen(argv[cmlen]) + 1;
  234.     enlen += 64;    /* filler for stuff like ARGV= and zeros, 
  235.              * minibuffer for empty param index conversion 
  236.              */
  237.     min_left = enlen;
  238.     for(cmlen = 0; envp[cmlen]; cmlen++)
  239.         enlen += strlen(envp[cmlen]) + 1;
  240.     enlen += 1024;    /* buffer for _unx2dos */
  241.  
  242. try_again:
  243.     if ((env = (char *)Malloc((long)enlen)) == NULL) {
  244.         errno = ENOMEM;
  245.         return -1;
  246.     }
  247.     left = enlen;
  248.     s = env;
  249.  
  250.     while ((p = *envp) != 0) {
  251. /*
  252.  * NOTE: in main.c, we converted environment variables which contain paths into
  253.  * POSIX form. Here, we convert back into gulam form. Note that the
  254.  * new variable can be longer than the old, so space _is_ a problem.
  255.  */
  256.         done = 0;
  257.         
  258.         if (pconv) {
  259.  
  260.             pc = pconv;
  261.             while (*pc) {
  262.                 
  263.                 tmp = pc; len = 0;
  264.                 while (*tmp && *tmp != ',') {
  265.                     tmp++; len++;
  266.                 }
  267.                 
  268.                 if (! strncmp(p, pc, len) && p[len] == '=') {
  269.                     len++;
  270.                     tmp = p + len;        /* tmp now after '=' */    
  271.                     cnt = 1;
  272.                     while (*tmp) {        /* count words */
  273.                         if (*tmp == ':')
  274.                             cnt++;
  275.                         tmp++;
  276.                     }
  277.  
  278.                     /* cnt * 2 is maximum enlargement when calling
  279.                      * _path_unx2dos. Make this agree with _unx2dos
  280.                      * in unx2dos.c
  281.                      */
  282.                     if (left - (strlen(p) + cnt * 2 + 1) < min_left)
  283.                         goto need_more_core;
  284.                         
  285.                     strncpy(s, p, len);
  286.                     _path_unx2dos(p + len, s + len);
  287.  
  288.                     while (*s) {
  289.                         s++; left--;
  290.                     }
  291.                     s++; left--;
  292.                     
  293.                     done = 1;
  294.                     break;    
  295.                 }
  296.                 
  297.                 if (! *tmp) break;
  298.                 pc = tmp + 1;
  299.             }
  300.         }
  301.         else if (!strncmp(p, "PATH=", 5)) {
  302.             strncpy(s, p, 5); s += 5; p += 5; left -= 5;
  303.  
  304.             tmp = p;        /* tmp now after '=' */    
  305.             cnt = 1;
  306.             while (*tmp) {        /* count words */
  307.                 if (*tmp == ':')
  308.                     cnt++;
  309.                 tmp++;
  310.             }
  311.  
  312.             /* cnt * 2 is maximum enlargement when calling
  313.              * _path_unx2dos. Make this agree with _unx2dos
  314.              * in unx2dos.c
  315.              */
  316.             if (left - (strlen(p) + cnt * 2 + 1) < min_left)
  317.                 goto need_more_core;
  318.                         
  319.             _path_unx2dos(p, s);
  320.  
  321.             while (*s) {
  322.                 s++; left--;
  323.             }
  324.             s++; left--;
  325.         
  326.             done = 1;
  327.         }            
  328.         
  329.         if (! done) {
  330.             /* copy variable without any conversion */
  331.             while (*p) {
  332.                 *s++ = *p++;
  333.                 if (--left <= min_left) {
  334. need_more_core:
  335.                     /* oh dear, we don't have enough core...
  336.                      * so we Mfree what we already have, and try again with
  337.                      * some more space.
  338.                      */
  339.                     Mfree(env);
  340.                     enlen += 1024;
  341.                     goto try_again;
  342.                 }
  343.             }
  344.             *s++ = 0;
  345.             left--;
  346.         }
  347.  
  348.         envp++;
  349.     }
  350.     
  351.     strcpy(s, "ARGV=");
  352.     s += 6; /* s+=sizeof("ARGV=") */
  353.  
  354.     if (argv && *argv) {
  355.         unsigned long null_params = 0;
  356.         int digits, i;
  357.         unsigned long idx, val;
  358.         char *const *ap;
  359.         
  360.         /* communicate empty arguments thru ARGV= value
  361.          */
  362.         for (ap = argv, idx = 0; 
  363.              *ap;
  364.              ap++, idx++)
  365.         {
  366.             if (! **ap) {
  367.                 /* empty argument found
  368.                  */
  369.                 if (! null_params) {
  370.                     strcpy(s-1, "NULL:");
  371.                     s += 4;    /* s now points after "NULL:" */
  372.                     left -= 6;
  373.                 } else {
  374.                     *s++ = ',' ;
  375.                 }
  376.                 null_params++;
  377.                                     
  378.                 /* convert index of zero param to ascii
  379.                  */
  380.                 if (idx == 0) {
  381.                     *s++ = '0';
  382.                     digits = 1;
  383.                 } else {
  384.                     digits = 0;
  385.                     val = idx;
  386.                     while (val) {
  387.                         for (i = digits; i > 0; i--)
  388.                             s[i] = s[i - 1];
  389.                         *s = "0123456789"[val % 10];
  390.                         val /= 10;
  391.                         digits++;
  392.                     }
  393.                     s += digits;
  394.                 }
  395.                 
  396.                 left -= digits + 2; /* 2 = sizeof( ',' in NULL:
  397.                                       * list + ' ' we put in place
  398.                                      * of empty params
  399.                                      */
  400.                 if (left < min_left)
  401.                     goto need_more_core;
  402.             }
  403.         }
  404.  
  405.         if (null_params) {
  406.             *s++ = 0;        /* finish "NULL:" list */
  407.         }
  408.  
  409.         /* copy argv[0] first (because it doesn't go into 
  410.          * the command line)
  411.          */
  412.         p = *argv;
  413.         if (! *p) {        /* if empty argument */
  414.             *s++ = ' ';    /* replace by space */
  415.         } else {
  416.              do {
  417.                 *s++ = *p++;
  418.              } while (*p);
  419.         }
  420.         *s++ = '\0';
  421.     }
  422.  
  423.     bzero(t = cmd, sizeof(cmd));
  424.  
  425. /* s points at the environment's copy of the args */
  426. /* t points at the command line copy to be put in the basepage */
  427.  
  428.     cmlen = 0;
  429.     if (argv && *argv) {
  430.         t++;
  431.         while (*++argv) {
  432.             p = *argv;
  433.             if (! *p) {    /* if empty argument */
  434.                  *s++ = ' ';    /* replace by space */
  435.                        /* write '' in TOS cmdlin
  436.                         */
  437.                  if (cmlen < TOS_ARGS) {
  438.                      *t++ = '\''; cmlen++;
  439.                  }
  440.                  if (cmlen < TOS_ARGS) {
  441.                      *t++ = '\''; cmlen++;
  442.                  }
  443.             } else {
  444.                  do {
  445.                     if (cmlen < TOS_ARGS) {
  446.                         *t++ = *p; cmlen++;
  447.                     }
  448.                     *s++ = *p++;
  449.                  } while (*p);
  450.             }
  451.             if (cmlen < TOS_ARGS && *(argv+1)) {
  452.                 *t++ = ' '; cmlen++;
  453.             }
  454.             *s++ = '\0';
  455.         }
  456. /*                *cmd = (char) cmlen;  NOT ANY MORE */
  457.     }
  458.  
  459.     /* tie off environment */
  460.     *s++ = '\0';
  461.     *s = '\0';
  462.  
  463.     /* signal Extended Argument Passing */
  464.     *cmd = 0x7f;
  465.  
  466. /* MiNT and MicroRTX support background processes with Pexec(100,...) */
  467. /* MiNT supports overlays with Pexec(200,...) */
  468.  
  469.     if (mode == P_NOWAIT) cmlen = 100;
  470.     else if (mode == P_OVERLAY && __mint) cmlen = 200;
  471.     else cmlen = 0;
  472.  
  473.     rval = Pexec((int)cmlen, path, cmd, env);
  474.  
  475.     if (rval < 0)
  476.     {
  477. #ifdef HASH_BANG
  478.         if (rval == -ENOEXEC) {
  479.             char    *umode;
  480.  
  481.              /* try to find UNIXMODE in environment */
  482.             if ((umode = getenv("UNIXMODE")) != NULL &&
  483.                     strchr(umode, 's') != NULL ) {
  484.                 (void)Mfree(env);
  485.                 return interpret_script(mode, path, _path, _argv, _envp);
  486.             }
  487.         }
  488. #endif
  489.         errno = (int) -rval;
  490.         rval = -1;
  491.     }
  492.     else if (mode == P_OVERLAY)
  493.     /* note that we get here only if MiNT is not active! */
  494.         _exit((int)rval);
  495.     (void)Mfree(env);
  496.     return (int) rval;
  497. }
  498.  
  499. #ifdef TEST
  500. int
  501. main (int argc, char **argv, char **envp)
  502. {
  503.     if (argc == 2) {
  504.         if (spawnve(P_WAIT, argv[1], &argv[1], envp) < 0) {
  505.             perror("spawn failed");
  506.             return errno;
  507.         }
  508.         return 0;
  509.     }
  510.     return 1;
  511. }
  512. #endif
  513.